//
// Created by v on 18-9-12.
//

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "cm_broadcast.h"
#include "utils/cm_utils.h"

struct CMBroadcast
{
    int       iSockFD;
    int       iPort;
    pthread_t iThread;
    int       iExit;

    struct sockaddr_in addrto;

    char szMac[32];
    char szIP[32];
    char szVersion[32];
    char szBroadMessage[128];
};

int cm_broadcast_init(CMBroadcastHandle *h, int port)
{
    struct CMBroadcast *in =
        (struct CMBroadcast *)cm_mem_malloc(sizeof(struct CMBroadcast));
    if (!in)
    {
        return -1;
    }
    memset(in, 0, sizeof(struct CMBroadcast));
    in->iPort = port;
    in->iExit = 0;

    *h = in;
    return 0;
}

int cm_broadcast_set_info(CMBroadcastHandle h, const char *mac, const char *ip,
                          const char *version)
{
    if (!h)
        return -1;
    sprintf(h->szMac, "%s", mac);
    sprintf(h->szIP, "%s", ip);
    sprintf(h->szVersion, "%s", version);
    sprintf(h->szBroadMessage, "%s,%s,%s,%s", "1.1", mac, ip, version);
    return 0;
}

int cm_broadcast_socket_init(CMBroadcastHandle h)
{
    if (!h)
        return -1;

    h->iSockFD = socket(AF_INET, SOCK_DGRAM, 0);
    if (h->iSockFD == -1)
    {
        return -2;
    }
    const int opt = 1;
    int nb = setsockopt(h->iSockFD, SOL_SOCKET, SO_BROADCAST, (char *)&opt,
                        sizeof(opt));
    if (nb == -1)
    {
        return -3;
    }
    int flags = fcntl(h->iSockFD, F_GETFL, 0);
    flags |= O_NONBLOCK;
    fcntl(h->iSockFD, F_SETFL, flags);

    memset(&h->addrto, 0, sizeof(h->addrto));
    h->addrto.sin_family      = AF_INET;
    h->addrto.sin_addr.s_addr = htonl(INADDR_BROADCAST);
    h->addrto.sin_port        = htons(h->iPort);

    return 0;
}

void *broadcast_thread(void *args)
{
    struct CMBroadcast *in = (struct CMBroadcast *)args;

    struct sockaddr_in addrto;
    memset(&addrto, 0, sizeof(addrto));
    addrto.sin_family      = AF_INET;
    addrto.sin_addr.s_addr = htonl(INADDR_BROADCAST);
    addrto.sin_port        = htons(in->iPort);

    int len = sizeof(addrto);

    char *msg = in->szBroadMessage;
    while (!in->iExit)
    {
        char ip[32] = {0};
        cm_get_ip_dev("wlan0", ip);
        if (strcmp(ip, in->szBroadMessage) != 0)
        {
            cm_broadcast_set_info(in, in->szMac, ip, in->szVersion);
        }

        int lenmsg = strlen(in->szBroadMessage);

        int ret = sendto(in->iSockFD, msg, lenmsg, 0,
                         (struct sockaddr *)&addrto, len);
        if (ret < 0)
        {

            //            printf("send error of message [%d][%s]\n", ret,
            //            strerror(errno));
        }
        else
        {
            //            printf("send success of message [%d]\n", ret);
        }
        sleep(30);
    }
    return NULL;
}

int cm_broadcast_socket_init_simple(CMBroadcastHandle h)
{
    return cm_broadcast_socket_init(h);
}

int cm_broadcast_bind(CMBroadcastHandle h)
{
    if (!h)
    {
        return -11;
    }
    if (h->iSockFD <= 0)
    {
        return -22;
    }

    int ret = bind(h->iSockFD, &h->addrto, sizeof(h->addrto));

    return ret;
}

int cm_broadcast_start(CMBroadcastHandle h)
{
    int ret = cm_broadcast_socket_init(h);
    if (ret < 0)
    {
        return ret;
    }

    ret = pthread_create(&h->iThread, NULL, broadcast_thread, h);
    if (ret < 0)
    {
        return -11;
    }
    pthread_detach(h->iThread);

    return 0;
}

int cm_broadcast_close(CMBroadcastHandle *h)
{
    if (h)
    {
        if ((*h)->iThread > 0)
        {
            pthread_cancel((*h)->iThread);
        }
        if ((*h)->iSockFD > 0)
        {
            close((*h)->iSockFD);
        }
        free(*h);
    }
    *h = NULL;
    return 0;
}

int cm_broadcast_send(CMBroadcastHandle h, char *data, size_t len)
{
    if (h->iSockFD < 0)
    {
        return -2;
    }

    int ret = sendto(h->iSockFD, data, len, 0,
                         (struct sockaddr *)&h->addrto, sizeof(h->addrto));
    return ret;
}

int cm_broadcast_recv(CMBroadcastHandle h, char *data, size_t len)
{
    if (h->iSockFD <= 0)
    {
        return -2;
    }

    unsigned int sock_len = sizeof(h->addrto);
    int ret = recvfrom(h->iSockFD, data, len, 0,
                     (struct sockaddr *)&h->addrto, &sock_len);
    return ret;
}
